#include "include/searcher.h"
#include "include/utils.h"

#include <algorithm>
#include <map>
#include <vector>


Searcher::Searcher() :
    offset_size_(0), total_doc_len_(0), doc_num_(0)
{
  cidx_file_ = Config::get_instance().get("cidx_file");
  tidx_file_ = Config::get_instance().get("tidx_file");
  string doc_info_file = Config::get_instance().get("doc_info_file");
  string word_info_file = Config::get_instance().get("word_info_file");

  load_doc_info(doc_info_file);
  load_word_info(word_info_file);    

  if (load_offset_list(cidx_file_, coffset_list_) == -1)
    exit(-1);
  if (load_offset_list(tidx_file_, toffset_list_) == -1)
    exit(-1);
  
  index_compr_ = new IndexCompressor(BYTECODEEX);
  cidx_cache_ = new IndexCache(offset_size_);
  tidx_cache_ = new IndexCache(offset_size_);
  weight_ = new Weight(doc_num_,total_doc_len_);
  seg_ = new Segment();
  //words_ht_ = idx->getDict();
  //doc_info_ = idx->getDocInfo();

}

Searcher::~Searcher() {
  delete []coffset_list_;
  delete []toffset_list_;
  delete cidx_cache_;
  delete tidx_cache_;
  delete index_compr_;
  delete weight_;
  delete []doc_info_;
  delete seg_;
  //  Segment::get_instance().release();
  Config::get_instance().release();
}

int Searcher::load_offset_list(const string& idx_file, int*& offset_list) {
  FILE* fin = fopen(idx_file.c_str(), "rb");
  if (!fin) {
    cerr << "can not open index file !" << endl;
    return -1;
  }

  fread((char*) &offset_size_, sizeof(int), 1, fin);
  offset_size_ /= sizeof(int);

  try {
    offset_list = new int[offset_size_];
  } catch (const std::bad_alloc &e) {
    cerr << "oom: " << e.what() << endl;
    return -1;
  }

  fseek(fin, 0, SEEK_SET);

  fread((char*) offset_list, sizeof(int), offset_size_, fin);

  if (ftell(fin) != offset_size_ * sizeof(int)) {
    cerr << "load index file error! " << endl;
    return -1;
  }

  fclose(fin);
}

int Searcher::search(const string& query, const int show_num, SRes* show_result) {
  map<int, float> result;
  map<int, float>::iterator mit;
  result.clear();

  vector<SegRes> res;
  //  Segment::get_instance().segment(query.c_str(), res);
  seg_->segment(query.c_str(), res); 
  vector<SegRes>::iterator it;
  for (it = res.begin(); it != res.end(); ++it) {
    if (!(it->type == 'C' || it->type == 'c' || it->type == 'x'
        || it->type == 'S'))
      continue;
    pair<int,int> info = get_info(it->word);

    int id = info.first;
    if (id < 0 || id >= offset_size_)
      continue;

    int df = info.second;
    
    char* encode_list = NULL;
    int len(0), useless(0), pos(0), pre_doc_id(0), doc_id(0), freq;

    len = get_posting(tidx_cache_, tidx_file_, toffset_list_, id,
        encode_list);

    while (pos < len) {
      doc_id = index_compr_->decode(encode_list, pos, useless);
      doc_id += pre_doc_id;
      pre_doc_id = doc_id;

      float s = weight_->score_idf(df);

      mit = result.find(doc_id);
      if (mit != result.end())
        mit->second += s * FACTOR; //TODO
      else
        result[doc_id] = s * FACTOR;
    }

    len = pos = pre_doc_id = doc_id = 0;
    len = get_posting(cidx_cache_, cidx_file_, coffset_list_, id,
        encode_list);
    
    while (pos < len) {
      doc_id = index_compr_->decode(encode_list, pos, useless);
      doc_id += pre_doc_id;
      pre_doc_id = doc_id;
      freq = index_compr_->decode(encode_list, pos, useless);

      float s = weight_->score_bm25(doc_id, df, it->times, freq,
          doc_info_[doc_id].doc_len);

      mit = result.find(doc_id);
      if (mit != result.end())
        mit->second += s;
      else
        result[doc_id] = s;
    }

  }

  vector<pair<int, float> > tmp_vec;
  tmp_vec.clear();
  mit = result.begin();
  for (; mit != result.end(); ++mit)
    tmp_vec.push_back(make_pair(mit->first, mit->second));

  sort(tmp_vec.begin(), tmp_vec.end(), is_greater);

  vector<pair<int, float> >::iterator vit = tmp_vec.begin();

  result.clear();

  for (int i = 0; i < tmp_vec.size() && i < show_num; ++i) { //top show_num
    int doc_id = tmp_vec[i].first;
    show_result[i].id = doc_id;
    show_result[i].score = tmp_vec[i].second;
    show_result[i].url = doc_info_[doc_id].url;
    show_result[i].title = doc_info_[doc_id].title;
  }

  return tmp_vec.size();
}

int Searcher::merge_list(char* encode_list, int len, vector<int>& result) {
  int useless = 0;
  int pos = 0;
  int doc_id, freq;
  vector<int> doc_list, res;
  doc_list.clear();
  res.clear();
  while (pos < len) {
    doc_id = index_compr_->decode(encode_list, pos, useless);
    freq = index_compr_->decode(encode_list, pos, useless);
    doc_list.push_back(doc_id);
  }
  set_intersection(result.begin(), result.end(), doc_list.begin(),
      doc_list.end(), back_inserter(res));
  result.clear();
  vector<int>::iterator it = res.begin();
  for (; it != res.end(); ++it)
    result.push_back(*it);
}

int Searcher::get_posting(IndexCache* idx_cache,
                         const string& idx_file,
                         int* offset_list,
                         int id,
                         char* &encode_list) {
  int len = 0;
  if (!idx_cache->find(id, len, encode_list)) {
    len = offset_list[id + 1] - offset_list[id];
    if (len > 0) {
      ifstream fin(idx_file.c_str(), ifstream::in);
      if (!fin) {
        cerr << "open index file error!" << endl;
        exit(-1);
      }
      fin.seekg(offset_list[id], std::ios::beg);
      try {
        encode_list = new char[len + 1];
      } catch (const std::bad_alloc &e) {
        cerr << "new encode_list error: " << e.what() << endl;
      }
      fin.read(encode_list, len); //loading posting
      encode_list[len] = '\0';
      fin.close();

      idx_cache->set(id, len, encode_list);
      return len;
    }
  }
  return len;
}

int Searcher::load_doc_info(const string& file) {
  ifstream fin(file.c_str(),ifstream::in);
  if(!fin) {
    cerr<<"can not open "<<file<<endl;
    return -1;
  }

  int i = 0;
  string line,word,title;

  getline(fin,line);
  doc_num_ = atoi(line.c_str());
  assert(doc_num_ > 0);
  
  getline(fin,line);
  total_doc_len_ = atoi(line.c_str());
  
  try {
    doc_info_ = new DocInfo[doc_num_];
  } catch(const std::bad_alloc &ex) {
    cerr<<"omm: "<<ex.what()<<endl;
  }
  
  while (i < doc_num_ && getline(fin,line)) {
    istringstream stream(line);
    stream>>word;
    doc_info_[i].doc_len = atoi(word.c_str());
    stream>>word;
    doc_info_[i].url = word;
    getline(fin,title);
    doc_info_[i].title = title;
    ++i;
  }

  fin.close();
  
  if (i != doc_num_) {
    cerr<<"load doc_info_file error."<<endl;
    return -1;
  }
  return 0;
}

int Searcher::load_word_info(const string& file) {
  ifstream fin(file.c_str(),ifstream::in);
  if(!fin) {
    cerr<<"can not open "<<file<<endl;
    return -1;
  }

  string line,word,key;
  int word_id,df;
  while (getline(fin,line)) {
    istringstream stream(line);
    stream>>key;
    stream>>word;
    word_id = atoi(word.c_str());
    stream>>word;
    df = atoi(word.c_str());
    pair<int,int> info(word_id,df);
    word_info_.insert(make_pair(key,info));
  }
  
  fin.close();
  return 0;
}

pair<int,int> Searcher::get_info(const string& word) {
  map<string,pair<int,int> >::iterator it = word_info_.find(word);
  if (it == word_info_.end()) {
    pair<int,int> error(-1,-1);
    return error;
  } else {
    return it->second;
  }
}
